/*
 *  BsdSocketAbstractionLayer.c
 *  NetTalk
 *
 *  Created by Andrey on 22/09/2008.
 *  Copyright 2008 ИУ7-2003. All rights reserved.
 *
 */

// BSD sockets stuff
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// Basic libc
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>

#include "BsdSocketAbstractionLayer.h"

int SetNetworkStreamToNonblockingMode(NTNetworkStream stream)
{
	int fileControlFlags;
	
	fileControlFlags = fcntl(stream, F_GETFL);
	if (fileControlFlags < 0)
		return fileControlFlags;
	
	fileControlFlags |= O_NONBLOCK;
	
	if (fcntl(stream, F_SETFL, fileControlFlags) < 0)
		return -1;
	
	return 0;
}

NTPeerInfoPtr NTAcceptConnection(NTNetworkStream stream)
{
	NTPeerInfoPtr newPeerInfo = calloc(1, sizeof(NTPeerInfo));
	
	struct sockaddr_in peerAddress;
	socklen_t peerAddressSize = sizeof(peerAddress);
	newPeerInfo->peerStream = accept(stream, (struct sockaddr*)&peerAddress, &peerAddressSize);
	if (newPeerInfo->peerStream == -1)
	{
		free(newPeerInfo);
		return NULL;
	}
	
	SetNetworkStreamToNonblockingMode(newPeerInfo->peerStream);
	
	newPeerInfo->peerAddress = strdup(inet_ntoa(peerAddress.sin_addr));
/*	
	char* peerAddressInAscii = inet_ntoa(peerAddress.sin_addr);
 
	size_t maxPeerAddressSize = strlen(peerAddressInAscii) + 1;
	newPeerInfo->peerAddress = malloc(maxPeerAddressSize * sizeof(wchar_t));
	mbstowcs(newPeerInfo->peerAddress, peerAddressInAscii, maxPeerAddressSize);
*/
	
	return newPeerInfo;
}

NTNetworkStream NTStartListeningOnPort(int port)
{
	NTNetworkStream resultStream;
	
	// Create underlying socket
	resultStream = socket(AF_INET, SOCK_STREAM, 0);
	if (resultStream < 0)
		return INVALID_STREAM;
	
	// Set socket options so that sockets will be reused
	int reuseAddress = 1;
	setsockopt(resultStream, SOL_SOCKET, SO_REUSEADDR, &reuseAddress, sizeof(reuseAddress));
	
	// Prepare sockaddr structure
	struct sockaddr_in listeningAddress;
	memset(&listeningAddress, 0, sizeof(listeningAddress));
	listeningAddress.sin_family = AF_INET;
	listeningAddress.sin_addr.s_addr = INADDR_ANY;
	listeningAddress.sin_port = htons(port);
	
	// Bind socket to port
	if (bind(resultStream, (struct sockaddr*)&listeningAddress, sizeof(listeningAddress)) < 0)
		return INVALID_STREAM;
	
	// Start actual listening
	if (listen(resultStream, MAX_CLIENT_BACKLOG) < 0)
		return INVALID_STREAM;
	
	SetNetworkStreamToNonblockingMode(resultStream);
	
	return resultStream;
}
